using System;
using System.Diagnostics;

namespace FluentParser.Expressions.Rules.Composition
{
    /// <summary>
    /// This expression is successful only if the two children expressions are
    /// successfully in a sequence.
    /// </summary>
    /// <typeparam name="TL">Type of the first expression</typeparam>
    /// <typeparam name="TR">Type of the second expression</typeparam>
    /// <typeparam name="T">Type of the resulting value</typeparam>
    /// <example><code>
    /// var name = ParseString.Regex("[A-Za-z]+");
    /// var number = ParseString.Regex("[0-9]+");
    /// var nameAndNumber = name + number;
    /// 
    /// name.Parse("SomeText")              --> "SomeText"
    /// number.Parse("1234")                --> "1234"
    /// nameAndNumber.Parse("SomeText")     --> failure
    /// nameAndNumber.Parse("1234")         --> failure
    /// nameAndNumber.Parse("SomeText1234") --> Tuple("SomeText", "1234")
    /// nameAndNumber.Parse("1234SomeText") --> failure
    /// </code></example>
    [DebuggerDisplay("[GroupExpressionRule {Left.GetType().Name} + {Right.GetType().Name}]")]
    public class GroupExpressionRule<TL, TR, T> : Expr<T> 
    {
        public BaseExpr<TL> Left { get; private set; }
        public BaseExpr<TR> Right { get; private set; }

        /// <summary> Function allowing to combine both resulting values into a single one </summary>
        public Func<TL, TR, T> Combine { get; private set; }

        public GroupExpressionRule(BaseExpr<TL> left, BaseExpr<TR> right, Func<TL, TR, T> combine)
        {
            Left = left;
            Right = right;
            Combine = combine;
            TypeOperation = OperationType.Grouping;
        }

        public override ParsingResult<T> Parse(GrammarContext context, int index)
        {
            ParsingResult<TL> value1 = Left.Parse(context, index);
            if (value1 != null)
            {
                ParsingResult<TR> value2 = Right.Parse(context, value1.End);
                if (value2 != null)
                    return ParsingResults.Build(index, value1.Length + value2.Length, Combine(value1.Value, value2.Value));
            }

            return null;
        }

        public override void ParseNode(GrammarContext context, ParsingNodeResult node)
        {
            ParsingNodeResult leftNode = node.AddChild(Left, context);
            if (leftNode.IsSuccess)
            {
                ParsingNodeResult rightNode = node.AddChild(Right, context, leftNode.End);
                if (rightNode.IsSuccess)
                {
                    node.Value = Combine((TL)leftNode.Value, (TR)rightNode.Value);
                    node.IsSuccess = true;
                }
                else
                {
                    node.Length = 0;
                }
            }
        }
    }
}